﻿using System;
using System.IO;

namespace ooWii.IO
{
	public class CisoStream : Stream
	{
		#region CisoStream

		Stream baseStream;
		SparseStream sparseStream;

		public CisoStream( Stream stream )
		{
			this.baseStream = stream;

			byte[] buffer = new byte[ 32768 ];
			if( stream.Read( buffer, 0, buffer.Length ) != buffer.Length ||
				WiiBuffer.GetString( buffer, 0, 4 ) != "CISO" )
			{
				throw new ArgumentException( "Not a CISO", "fileName" );
			}

			UInt32 wbfs_sec_sz = ( (UInt32)buffer[ 4 ] ) | ( (UInt32)buffer[ 5 ] << 8 ) | ( (UInt32)buffer[ 6 ] << 16 ) | ( (UInt32)buffer[ 7 ] << 24 );

			sparseStream = new SparseStream( stream );

			long physical = buffer.Length;
			int wbfs_sec_max = 0;

			for( int i = 0; i < buffer.Length - 8; i++ )
			{
				if( buffer[ i + 8 ] != 0 )
				{
					sparseStream.Map( i * wbfs_sec_sz, physical, wbfs_sec_sz );
					physical += wbfs_sec_sz;

					wbfs_sec_max = i;
				}
			}

			const long layerSize = 143432L * 0x8000;
			long length = wbfs_sec_max * wbfs_sec_sz;
			long layers = ( ( length + layerSize ) - 1 ) / layerSize;

			sparseStream.SetLength( layers * layerSize );
		}

		#endregion

		#region Stream

		public override bool CanRead
		{
			get
			{
				return sparseStream.CanRead;
			}
		}

		public override bool CanSeek
		{
			get
			{
				return sparseStream.CanSeek;
			}
		}

		public override bool CanWrite
		{
			get
			{
				return false;
			}
		}

		public override void Flush()
		{
			sparseStream.Flush();
		}

		public override long Length
		{
			get
			{
				return sparseStream.Length;
			}
		}

		public override long Position
		{
			get
			{
				return sparseStream.Position;
			}
			set
			{
				sparseStream.Position = value;
			}
		}

		public override int Read( byte[] buffer, int offset, int count )
		{
			return sparseStream.Read( buffer, offset, count );
		}

		public override long Seek( long offset, SeekOrigin origin )
		{
			return sparseStream.Seek( offset, origin );
		}

		public override void SetLength( long value )
		{
			throw new NotSupportedException();
		}

		public override void Write( byte[] buffer, int offset, int count )
		{
			throw new NotSupportedException();
		}

		public override void Close()
		{
			baseStream.Close();
			base.Close();
		}

		#endregion
	}
}
